Skip to content

Add light mode theme toggle#46

Draft
bbethell-1 wants to merge 188 commits into
mainfrom
revert-43-revert-42-fix/light-mode
Draft

Add light mode theme toggle#46
bbethell-1 wants to merge 188 commits into
mainfrom
revert-43-revert-42-fix/light-mode

Conversation

@bbethell-1

Copy link
Copy Markdown
Contributor

Reverts #43

stencell and others added 30 commits May 15, 2026 11:45
Promote main to production
Extract infrastructure metadata from AgnosticD v2 CRDs during
catalog refresh for Publishing House express mode queries.

- Alembic migration 002: new columns on catalog_items (is_agd_v2,
  agd_config, cloud_provider, ocp_version, os_image, instances_json,
  sizing), 4 new tables (catalog_item_workloads, workload_mapping,
  workload_aliases, catalog_item_acl_groups)
- V2 detection via exact scm_url match, FQCN workload parsing
- Handles both OCP items (flat workloads list) and RHEL/VM items
  (dict-based software_workloads + instances + os_image)
- 35 verified workload mappings + 25 product aliases in seed file
- CLI: rcars infra stats, rcars workload {sync,unmapped,map,alias,list}
- GET /catalog/{ci_name} enriched with workloads + ACL groups
- Design spec at docs/superpowers/specs/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The CLI refresh path had the same upsert loop as the worker but
was missing the _workloads/_acl_groups pop-and-sync step added
to ops.py. Without this, CLI-triggered refreshes populated the
infra columns but not the junction tables.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
alembic/ and alembic.ini were at the repo root but the API container
build uses contextDir=src/api, so migrations were never available in
the container image. Moved both into src/api/ where they ship with
the app via COPY . . in the Containerfile.

Updated Ansible --tags migrate to run rcars init-db (CREATE TABLE
IF NOT EXISTS for new installs) then alembic upgrade head (ALTER
TABLE for existing schemas). This makes schema changes deployable
to production without dropping the database.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Alembic was moved from repo root to src/api/ so it ships in the
container image. Updated all documentation to reflect the new
location and the Ansible migrate workflow (rcars init-db + alembic
upgrade head).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Alembic's context.configure() requires a SQLAlchemy connection, not
a raw psycopg3 connection. Use create_engine() with postgresql+psycopg
dialect (psycopg3) instead of psycopg.connect() directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… (Session 2)

API endpoints:
- GET /catalog/search/infrastructure — faceted search by workloads,
  config, cloud_provider, ocp_version, os_image (alias resolution)
- GET /catalog/facets — available filter values with counts
- GET/POST/DELETE /catalog/workload-mappings — mapping CRUD
- GET /catalog/infra-stats — v2/workload coverage stats
- POST /admin/scan-workloads — trigger workload repo scan

Workload scanner (services/workload_scanner.py):
- Clones agDv2 collection repos from github.com/agnosticd/*
- Reads role code (defaults, tasks, templates) for each role
- LLM analysis via Haiku to determine product/description/category
- SHA-based change detection via ls-remote (skip unchanged repos)
- Upserts verified mappings, skips infrastructure plumbing roles

Pipeline integration:
- run_workload_scan() worker function (ops.py)
- Step 4 in nightly maintenance pipeline (gated on workload_scan_enabled)
- Registered in WorkerSettings with 1hr timeout
- CLI: rcars workload scan [--collection X] [--force]

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New docs/architecture/schema-reference.md with full column-level
  details for all 14 tables (including 5 new infra tables)
- system-design.md: replaced inline table descriptions with summary
  table + link to reference. Added Infrastructure Metadata Extraction
  section covering v2 detection, workload scanning, faceted search.
  Restored full vector embeddings explanation.
- CLAUDE.md: updated schema table (14 tables), API endpoints (35),
  CLI commands (infra + workload groups), nightly pipeline (4 steps)
- cli-guide.md: added rcars infra and rcars workload command docs
  with examples and explanations
- operations.md: updated pipeline from 3 to 4 steps, added workload
  scan change detection explanation, RCARS_WORKLOAD_SCAN_ENABLED var
- mkdocs.yml: added Schema Reference to nav
- Fixed .gitignore: /OPERATIONS.md scoped to root only

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Browse page:
- [v2] badge on AgnosticD v2 item headers
- Infrastructure detail panel in expanded items: config type, cloud
  provider, OCP version, OS image, workloads list, ACL groups
- "v2" filter toggle to show only AgnosticD v2 items

Admin page:
- "Workload Repos" section with Scan button (job polling + log)
- Infrastructure stats in Catalog Status table: v2 items, workloads
  coverage, mapped/verified/unmapped role counts

API client:
- 8 new methods for infrastructure search, facets, workload mappings,
  infra stats, and workload scanning
- Extended CatalogItem/ItemDetail interfaces with v2 infra fields

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Infrastructure-aware catalog metadata marked complete (deployed to
dev). Remaining UI items (filter dropdowns, mapping management table,
combined query) moved to separate backlog section. Full session
handoff notes in WORKLOG.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removes [esbuild](https://github.com/evanw/esbuild). It's no longer used after updating ancestor dependencies [esbuild](https://github.com/evanw/esbuild), [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). These dependencies need to be updated together.


Removes `esbuild`

Updates `@vitejs/plugin-react` from 4.7.0 to 6.0.2
- [Release notes](https://github.com/vitejs/vite-plugin-react/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@6.0.2/packages/plugin-react)

Updates `vite` from 6.4.2 to 8.0.16
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.16/packages/vite)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version:
  dependency-type: indirect
- dependency-name: "@vitejs/plugin-react"
  dependency-version: 6.0.2
  dependency-type: direct:development
- dependency-name: vite
  dependency-version: 8.0.16
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
- Design spec for Browse page filter system overhaul: collapsible filter
  panel with Cloud Provider, Workloads (multi-select), and AgnosticD
  Config dropdowns; server-side filtering replacing client-side;
  numbered pagination; role-based curator filter separation
- Backlog item for future contextual sidebar navigation redesign

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8 tasks: backend filtered query, route params, API client, pagination
component, workload multi-select, BrowsePage rewrite, admin deep links,
manual testing checklist.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… filters

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ilter params

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update listCatalog API client with search/stage/infra/content_filter params
- Add Pagination component with numbered pages and ellipsis
- Add WorkloadMultiSelect dropdown with checkboxes
- Add CSS for filter panel, curator panel, pagination, and multi-select

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…gination

- Replace client-side load-all with server-side filtered queries
- Collapsible filter panel: Cloud Provider, Workloads (multi-select),
  AgnosticD Config dropdowns with dismissable filter chips
- Curator-only filter panel (amber): Unanalyzed, Failures, Stale, Needs Review
- Numbered pagination replacing prev/next buttons
- URL state sync for shareable filtered views
- Debounced search (300ms)
- Update Admin deep links from ?filter= to ?content_filter=

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace monolithic Catalog Status table with 3 stat cards
  (Catalog, Analysis, Infrastructure) in responsive grid
- Add Workload Mapping Management section with mapped/unmapped
  tables, inline Map form, and delete capability
- Reorder sections: Status cards at top, operations grouped,
  workload scan and mappings at bottom
- Flexible-width layout scaling with browser window

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three tabs: Status (stat cards + scheduled maintenance),
Sync & Analysis (catalog sync + content analysis + full re-analysis),
Workloads (workload scan + mapping management).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…step

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move recent jobs table into Sync & Analysis as a collapsible section.
Remove Workers from sidebar navigation. Old /admin/workers URL
redirects to /admin/catalog.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Browse page redesign and admin reorganization complete.
Combined query deferred with rationale.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
stencell and others added 26 commits June 18, 2026 12:18
Cost per provision is a curator/platform metric, not useful for content
selection. A field seller picking labs for a conference doesn't need to
know that a lab costs $300 to provision — and the number without context
is confusing. The metric is still on the retirement dashboard where it
belongs. Also renamed "provisions" to "deployments" for clarity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rec card cleanup + usage-aware scoring boost
The native title tooltip takes 2-3 seconds to appear and shows a
cursor:help question mark that feels unresponsive. Replaced with a
clickable ⓘ icon that toggles the explanation text inline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
30 published/base pairs show as duplicate entries with split metrics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove github-source Secret from manifests-infra.yaml.j2
- Remove sourceSecret conditionals from both BuildConfigs
- Remove github_token and webhook_secret from .example vars files
- Update github_repo to rhpds/rcars in .example files
- Remove github_token from deployment docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Published CIs (Virtual CIs) and their base CIs appeared as separate
entries with split metrics — the published got provisions/sales while
the base got dev/event cost, making base CIs score as high retirement
candidates (65-80) even though they're actively needed. 29 pairs affected.

Adds post-merge step that detects pairs via published_ci_name field,
combines their metrics under the published entry, and removes the base.
Also excludes base CIs from catalog backfill to prevent re-adding with
zeros.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents silent dict overwrites when multiple stage variants of a base
CI point to different published CIs. Prefers the prod stage link.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add try/except fallback in call_llm() so LiteMaaS failures fall back
  to Vertex/Anthropic instead of crashing the request
- Cap recommendation scores at 0-100 in usage boost and duration penalty
- Add frontend safety clamp in RecCard to prevent >100% display
- Add checksum/secrets annotation to pod templates so secret value
  changes trigger automatic rollouts on --tags apply
- Add end-to-end advisor smoke test to Ansible deployer (runs on
  deploy, apply, and smoke-test tags)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add missing Retirement Analysis section to web guide with full
  dashboard walkthrough (time windows, prod/without-prod tabs, scoring)
- Fix Browse page docs: add Filters panel, v2 badge, infrastructure
  details, restructure into subsections matching actual UI
- Fix Content Analysis docs: filter pills → dropdowns, add search
- Fix Admin docs: add all 5 stat cards, Workloads tab, Recent Jobs
- Restructure deployment guide: brief architecture recap with link to
  system-design.md, remove local paths, use plain oc exec pattern,
  absorb operational workflows from CLI guide
- Restructure CLI guide: move env vars to bottom in grouped tables,
  remove operational workflows (now in deployment), verify all 25
  commands against cli.py
- Remove content overlap section from operations guide (redundant
  with architecture doc), remove local worker instructions
- Delete development guide (no local dev workflow needed)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Smoke test: add -f and --max-time 30 to curl calls so HTTP errors
  and hangs are caught properly
- Checksum annotations: remove oauth_client_secret from API/worker pod
  checksums since those pods don't consume that secret
- Usage boost: add max(0, ...) lower bound for consistency with
  duration_penalty clamping

Skipped: 24-candidate threshold assertion — candidate count depends
on catalog size and query, not a fixed deployment contract.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Overlap report was returning pairs from all stages regardless of
which stage was selected. Switching between prod/event/dev and
recomputing left stale pairs from other stages in the view.

- Add stage parameter to get_overlap_report() and get_similarity_stats()
- Pass stage filter through the /admin/overlap API endpoint
- Frontend passes selected stage when fetching and reloads on stage change

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove developer filesystem paths from WORKLOG.md and strip hardcoded
password from alembic.ini default connection string (overridden by
env.py at runtime anyway).

Closes #20

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Closes #22

- XSS: escape HTML entities before markdown rendering in AdvisorPage.tsx
- SQL injection: validate start_date matches ISO format (YYYY-MM-DD) in
  all _build_*_sql() functions in reporting_sync.py
- SSRF: validate URL scheme and reject private/internal IPs in
  event_parser.py _fetch_html()
- URL scheme: restrict clone_showroom() to https:// URLs only in
  analyzer.py
- Pydantic: use HttpUrl type for OverrideUrlRequest in catalog.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Redact secrets and credentials from tracked files
Security: Input sanitization — XSS, SSRF, SQL injection
…content

Items without a Showroom URL (like Portworx Labs) were invisible to the
advisor because they never received embeddings. The scan pipeline required
a showroom_url to generate analysis and embeddings, leaving metadata-only
catalog items completely unsearchable.

This adds a metadata embedding pass to the scan command that generates
embeddings from available catalog fields (display_name, description,
category, product) for items that have no Showroom URL and no existing
embeddings.

Changes:
- analyzer.py: add build_metadata_embedding_text() to construct embedding
  text from catalog metadata fields
- database.py: add get_items_needing_metadata_embedding() to find catalog
  items without Showroom URLs that lack embeddings
- cli.py: add metadata embedding pass after the main Showroom analysis loop
  in the scan command, ensuring it runs even when there are no Showroom
  items to analyze

Closes #38

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce a light/dark theme toggle for the RCARS UI. Users who prefer
light mode can now switch via a sun/moon button in the header bar.

- Create useTheme hook with React context, localStorage persistence,
  and data-theme attribute on <html>
- Convert hardcoded dark colors in lcars.css to CSS custom properties
- Add [data-theme="light"] overrides with light-friendly palette
- Wire ThemeProvider into App.tsx and add toggle button to LcarsHeader
- Update inline styles in header menu to use CSS variables

Closes #37

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add light mode theme toggle
Generate embeddings from catalog metadata for items without Showroom …
Revert "Add light mode theme toggle"
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added theme toggle button in the header to switch between light and dark modes.
    • User theme preference is automatically saved and restored on return visits.
  • Style

    • Updated all interface components with dynamic theme-aware styling using CSS variables for consistent appearance across both themes.

Walkthrough

Adds a light/dark theme system to the RCARS frontend. A new useTheme.ts hook manages theme state via localStorage and document.documentElement's data-theme attribute. App.tsx wraps the app in ThemeContext.Provider, LcarsHeader gains a sun/moon toggle button, and lcars.css replaces hardcoded colors with CSS custom properties across all UI components for both themes.

Changes

Light/Dark Theme System

Layer / File(s) Summary
Theme context and hook
src/frontend/src/hooks/useTheme.ts
Defines Theme, ThemeState, and ThemeContext with dark defaults. useTheme() reads the context; useThemeProvider() initializes from localStorage, syncs data-theme on document.documentElement, and exposes toggleTheme().
App provider wiring and CSS design tokens
src/frontend/src/App.tsx, src/frontend/src/styles/lcars.css
App.tsx calls useThemeProvider() and adds ThemeContext.Provider around existing providers; loading screen switches to CSS variable colors. lcars.css adds all new design tokens under :root and [data-theme="light"].
Header theme toggle button and variable-based styling
src/frontend/src/components/lcars/LcarsHeader.tsx, src/frontend/src/styles/lcars.css
LcarsHeader adds a sun/moon toggle button calling toggleTheme and replaces all hardcoded inline styles (menu border, dropdown background/shadow, separator, SVG logo fills) with CSS variable references. CSS adds .theme-toggle and .curator-toggle variable-based rules.
CSS component color variable replacement
src/frontend/src/styles/lcars.css
Replaces hardcoded colors with CSS variables across navigation, chat, recommendations, curator/admin/filter panels, workload multiselect, LCARS toggle, and content-analysis table/card selectors.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: adding a light mode theme toggle feature that encompasses all modifications across App.tsx, hooks, CSS variables, and header components.
Description check ✅ Passed The description references reverting PR #43, which directly relates to the changeset's purpose of reintroducing the light mode theme toggle feature.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch revert-43-revert-42-fix/light-mode

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@bbethell-1 bbethell-1 marked this pull request as draft June 22, 2026 14:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/frontend/src/components/lcars/LcarsHeader.tsx (1)

158-164: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Consider adding aria-label for better screen reader support.

The theme toggle button currently uses a title attribute which provides a tooltip on hover, but adding an aria-label would improve accessibility for screen reader users.

♿ Suggested accessibility enhancement
 <button
   className="theme-toggle"
   onClick={toggleTheme}
   title={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
+  aria-label={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
 >
   {theme === 'dark' ? '\u2600' : '\u263D'}
 </button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/frontend/src/components/lcars/LcarsHeader.tsx` around lines 158 - 164,
The theme toggle button with className="theme-toggle" and onClick={toggleTheme}
currently only has a title attribute for accessibility, but lacks an aria-label
attribute which is essential for screen reader users. Add an aria-label
attribute to the button element that provides the same descriptive text as the
existing title attribute, ensuring it clearly indicates the current theme state
and what action will occur when clicked by screen reader users.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/frontend/src/components/lcars/LcarsHeader.tsx`:
- Around line 158-164: The theme toggle button with className="theme-toggle" and
onClick={toggleTheme} currently only has a title attribute for accessibility,
but lacks an aria-label attribute which is essential for screen reader users.
Add an aria-label attribute to the button element that provides the same
descriptive text as the existing title attribute, ensuring it clearly indicates
the current theme state and what action will occur when clicked by screen reader
users.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 64ca0ae1-56bf-4239-93b9-9d771deaff53

📥 Commits

Reviewing files that changed from the base of the PR and between 00b2c2f and f660796.

📒 Files selected for processing (4)
  • src/frontend/src/App.tsx
  • src/frontend/src/components/lcars/LcarsHeader.tsx
  • src/frontend/src/hooks/useTheme.ts
  • src/frontend/src/styles/lcars.css

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants